// Value is recorded
#include <string.h>
#include "skip_conf.h"
#include "skip_file.h"

typedef struct _data_slot{
	RECPOS next;
	int data_size;
	byte* data;			// maxsize is SLOT_DATA_SIZE
}data_slot;

#pragma pack (1)
typedef struct _slot_tag{
	RECPOS next;
	int data_size;
	byte data[1];
}slot_tag;
#pragma pack () 

// slot cache 
typedef struct _slot_cache{
	slot_tag* tag_p;
	RECPOS pos;
}slot_cache;

typedef struct _data_desc{
	RECPOS	head;
	int		fd;
	int		slot_size;
	int		cache_size;
	slot_cache*  cache_p;
	slice sl_out;
}data_desc;


#define  SLOT_HEAD_SIZE			(sizeof(RECPOS)+sizeof(int))
#define  SLOT_DATA_SIZE(dp)		((int)((dp)->slot_size-sizeof(int)-sizeof(RECPOS)))
#define  SLOT_NEXT_POS(start)	(start)
#define	 SLOT_SIZE_POS(start)	(start+sizeof(RECPOS))
#define  SLOT_DATA_POS(start)	(SLOT_SIZE_POS(start)+sizeof(int))	
#define  check_pos(dp, pos)		(!((pos)<sizeof(RECPOS) || (((pos)-sizeof(RECPOS))%(dp)->slot_size)!=0))
#define  _slot_idx(dp, pos)		(((pos)-sizeof(RECPOS))/((dp)->slot_size))
#define  cache_at(dp, pos)		(&(((dp)->cache_p)[_slot_idx(dp, pos)%(dp)->cache_size]))


static void _skipD_writeslot(data_desc* dp, RECPOS start, data_slot* slot);

// data open
data_desc* skipD_open(const char* file, int slot_size, int cache_size)
{
	int i;
	data_desc* ret;
	size_t d_size=s_filesize(file);
	data_desc dd = {0};
	dd.head = R_NULL;
	dd.slot_size = slot_size;
	dd.cache_size = cache_size;

	if(file==NULL || cache_size<=0 || slot_size <=SLOT_HEAD_SIZE)
		return NULL;

	if(d_size<sizeof(RECPOS))
	{
		dd.fd = s_create(file);		// create data file
		s_write(dd.fd, R_START, (byte*)(&dd.head), sizeof(dd.head));	// write slot head
	}
	else
	{
		dd.fd = s_open(file);
		s_read(dd.fd, R_START, (byte*)(&dd.head), sizeof(dd.head));
	}

	// malloc cahce
	dd.cache_p = (slot_cache*)malloc(sizeof(slot_cache)*cache_size);
	for(i=0; i<cache_size; i++)
	{
		dd.cache_p[i].pos = R_NULL;
		dd.cache_p[i].tag_p = (slot_tag*)malloc(slot_size);
	}

	ret = (data_desc*)malloc(sizeof(*ret));
	*ret = dd;
	
	return ret;
}

// data close
void skipD_close(data_desc* dp)
{
	int i;
	if(dp==NULL)
		return;
	for(i=0; i<dp->cache_size; i++)
		free(dp->cache_p[i].tag_p);
	free(dp->cache_p);
	s_close(dp->fd);
	free(dp);
}

// data write
RECPOS skipD_write(data_desc* dp, slice* sl)
{
	RECPOS ret;
	if( dp==NULL || sl==NULL || 
		sl->size > SLOT_DATA_SIZE(dp)
	   )
		return	R_NULL;
	
	if(dp->head==R_NULL)
	{
		RECPOS pos = lseek(dp->fd, 0L, SEEK_END);
		data_slot slot = {R_NULL, sl->size, sl->buf};
		_skipD_writeslot(dp, pos, &slot);
		ret = pos;
	}
	else
	{
		RECPOS n_pos; 
		data_slot slot;
		s_read(dp->fd, SLOT_NEXT_POS(dp->head), (byte*)(&n_pos), sizeof(RECPOS));
		slot.next = R_NULL;
		slot.data = sl->buf;
		slot.data_size = sl->size;
		_skipD_writeslot(dp, dp->head, &slot);
		ret = dp->head;
		dp->head = n_pos;
		s_write(dp->fd, R_START, (byte*)(&n_pos), sizeof(n_pos));
	}
	return ret;
}

// data read
slice* skipD_read(data_desc* dp, RECPOS pos)
{
	slot_cache* sc;
	if( dp==NULL || !check_pos(dp, pos))
		return NULL;

	sc = cache_at(dp, pos);
	if(sc->pos != pos)
	{
		s_read(dp->fd, pos, (byte*)(sc->tag_p), dp->slot_size);
		sc->pos = pos;
	}
	dp->sl_out.buf = sc->tag_p->data;
	dp->sl_out.size = sc->tag_p->data_size;
	return &(dp->sl_out);
}

// data del
int skipD_del(data_desc* dp, RECPOS pos)
{
	slot_cache* sc;

	if( dp==NULL || !check_pos(dp, pos))
		return SKIP_FALSE;
	sc = cache_at(dp, pos);
	if(sc->pos == pos)
		sc->pos = R_NULL;

	s_write(dp->fd, SLOT_NEXT_POS(pos), (byte*)(&dp->head), sizeof(RECPOS));
	dp->head = pos;
	s_write(dp->fd, R_START, (byte*)(&pos), sizeof(RECPOS));
	return SKIP_TRUE;
}

static void _skipD_writeslot(data_desc* dp, RECPOS start, data_slot* slot)
{
	slot_cache* sc = cache_at(dp, start);
	sc->pos = start;
	sc->tag_p->next = slot->next;
	sc->tag_p->data_size = slot->data_size;
	memcpy(sc->tag_p->data, slot->data, slot->data_size);
	s_write(dp->fd, start, (byte*)(sc->tag_p), dp->slot_size);
}
